//
//  YCFileStorageManager.m
//  Pods
//
//  Created by Apple on 2022/11/12.
//

#import "YCFileStorageManager.h"
#import <CommonCrypto/CommonCrypto.h>
#import <CommonCrypto/CommonDigest.h>

NSString * const FileStorageDefaultKey = @"fjyryc0123456789";

@interface YCFileStorageManager ()

@property (nonatomic, copy) NSString *documentPath;
@property (nonatomic, strong) dispatch_queue_t queue;

@end

@implementation YCFileStorageManager

+ (instancetype)manager {
    static YCFileStorageManager *manager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[self alloc ] init];
    });
    return manager;
}

- (instancetype)init {
    if (self = [super init]) {
        self.queue = dispatch_queue_create("YCFileStorageManager_readwrite_queue", DISPATCH_QUEUE_CONCURRENT);
    }
    return self;
}

// 同步接口，文件是否存在
- (BOOL)fileExists:(NSString *)name {
    __block BOOL res;
    dispatch_sync(self.queue, ^{
        NSFileManager *fileManager = [NSFileManager defaultManager];
        NSString *realPath = [self.documentPath stringByAppendingPathComponent:name];
        res = [fileManager fileExistsAtPath:realPath];
    });
//    dispatch_get_current_queue();
    return res;
}

#pragma mark -- 公有方法 ---
- (void)writeFile:(NSData *)data name:(NSString *)name {
    NSString *realPath = [self.documentPath stringByAppendingPathComponent:name];
    NSFileManager *fileMgr = [NSFileManager defaultManager];
    if (![fileMgr fileExistsAtPath:realPath]) {
        BOOL ret = [fileMgr createDirectoryAtPath:self.documentPath withIntermediateDirectories:YES attributes:nil error:nil];
        if (ret) {
            NSLog(@"文件创建成功");
        } else {
            NSLog(@"文件创建失败");
            return;
        }
    }
    NSData *encryptData = [self encryptDataWithData:data Key:self.fileStorageKey?:FileStorageDefaultKey];
    [encryptData writeToFile:realPath atomically:YES];
}

// 写入文件，文件名是 name，当异步写入完成时，会回调 completion。result 返回是否成功
- (void)writeFile:(NSData *)data name:(NSString *)name completion:(nullable void(^)(BOOL result))completion {
    dispatch_barrier_async(self.queue, ^{
        NSString *realPath = [self.documentPath stringByAppendingPathComponent:name];
        NSFileManager *fileMgr = [NSFileManager defaultManager];
        if (![fileMgr fileExistsAtPath:realPath]) {
            BOOL ret = [fileMgr createDirectoryAtPath:self.documentPath withIntermediateDirectories:YES attributes:nil error:nil];
            if (ret) {
                NSLog(@"文件创建成功");
            } else {
                NSLog(@"文件创建失败");
                return;
            }
        }
        NSData *encryptData = [self encryptDataWithData:data Key:self.fileStorageKey?:FileStorageDefaultKey];
        BOOL success = [encryptData writeToFile:realPath atomically:YES];
        dispatch_async(dispatch_get_main_queue(), ^{
            if (completion) {
                completion(success);
            }
        });
    });
}

// 异步读取文件，文件名是 name，完成时会将文件数据在 completion 返回
- (void)readFile:(NSString *)name completion:(nullable void(^)(NSData *data))completion {
    dispatch_async(self.queue, ^{
        NSString *realPath = [self.documentPath stringByAppendingPathComponent:name];
        NSData *data = [[NSData alloc] initWithContentsOfFile:realPath];
        NSData *decryptData = [self decryptDataWithData:data andKey:self.fileStorageKey?:FileStorageDefaultKey];
        dispatch_async(dispatch_get_main_queue(), ^{
            if (completion) {
                completion(decryptData);
            }
        });
    });
}

// 同步读文件
- (NSData *)readFile:(NSString *)name {
    __block NSData *decryptData;
    dispatch_sync(self.queue, ^{
        NSString *realPath = [self.documentPath stringByAppendingPathComponent:name];
        NSData *data = [[NSData alloc] initWithContentsOfFile:realPath];
        decryptData = [self decryptDataWithData:data andKey:self.fileStorageKey?:FileStorageDefaultKey];
    });
    return decryptData;
}

// 同步删除文件
- (void)removeFile:(NSString *)name {
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSString *realPath = [self.documentPath stringByAppendingPathComponent:name];
    [fileManager removeItemAtPath:realPath error:nil];
}


// 异步删除文件
- (void)removeFile:(NSString *)name completion:(nullable void(^)(BOOL result))completion {
    dispatch_barrier_async(self.queue, ^{
        NSFileManager *fileManager = [NSFileManager defaultManager];
        NSString *realPath = [self.documentPath stringByAppendingPathComponent:name];
        BOOL res = [fileManager removeItemAtPath:realPath error:nil];
        dispatch_async(dispatch_get_main_queue(), ^{
            if (completion) {
                completion(res);
            }
        });
    });
}

// 异步删除所有文件
- (void)removeAllFilesWithCompletion:(nullable void(^)(BOOL result))completion {
    dispatch_barrier_async(self.queue, ^{
        NSFileManager *fileManager = [NSFileManager defaultManager];
        NSDirectoryEnumerator *enumPath = [fileManager enumeratorAtPath:self.documentPath];
        NSError *err = nil;
        BOOL res;
        NSString *file;
        while(file = [enumPath nextObject]) {
            res = [fileManager removeItemAtPath:[self.documentPath stringByAppendingPathComponent:file] error:&err];
            if(!res) {
                NSLog(@"异步删除所有文件出错：%@", err);
            }
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            if (completion) {
                completion(YES);
            }
        });
    });
}

- (NSString *)documentPath {
    if (!_documentPath) {
        NSString *rootPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
        _documentPath = [rootPath stringByAppendingPathComponent:@"YCFileStorageDocumentPath"];
    }
    return _documentPath;
}

//对NSData 进行加密
- (NSData *)encryptDataWithData:(NSData *)data Key:(NSString *)key {
    // 设置秘钥
    NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
    uint8_t cKey[kCCKeySizeAES128];
    bzero(cKey, sizeof(cKey));
    [keyData getBytes:cKey length:kCCKeySizeAES128];
    
    // 设置iv
    uint8_t cIv[kCCBlockSizeAES128];
    bzero(cIv, kCCBlockSizeAES128);
    
    int option = kCCOptionPKCS7Padding | kCCOptionECBMode;
    
    // 设置输出缓冲区
    
    size_t bufferSize = [data length] + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);
    
    // 开始加密
    size_t encryptedSize = 0;
    
    /**
     @constant   kCCAlgorithmAES     高级加密标准，128位(默认)
     @constant   kCCAlgorithmDES     数据加密标准
     */
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
                                          kCCAlgorithmAES,
                                          option,
                                          cKey,
                                          kCCKeySizeAES128,
                                          cIv,
                                          [data bytes],
                                          [data length],
                                          buffer,
                                          bufferSize,
                                          &encryptedSize);
    
    NSData *result = nil;
    if (cryptStatus == kCCSuccess) {
        result = [NSData dataWithBytesNoCopy:buffer length:encryptedSize];
    } else {
        free(buffer);
    }
    return result;
}

// 解密
- (NSData *)decryptDataWithData:(NSData *)data andKey:(NSString *)key {
    // 设置秘钥
    NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
    uint8_t cKey[kCCKeySizeAES128];
    bzero(cKey, sizeof(cKey));
    [keyData getBytes:cKey length:kCCKeySizeAES128];
    
    // 设置iv
    uint8_t cIv[kCCBlockSizeAES128];
    bzero(cIv, kCCBlockSizeAES128);
    
    int option = kCCOptionPKCS7Padding | kCCOptionECBMode;
    
    // 设置输出缓冲区，options参数很多地方是直接写0，但是在实际过程中可能出现回车的字符串导致解不出来
    size_t bufferSize = [data length] + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);
    
    // 开始解密
    size_t decryptedSize = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
                                          kCCAlgorithmAES128,
                                          option,
                                          cKey,
                                          kCCKeySizeAES128,
                                          cIv,
                                          [data bytes],
                                          [data length],
                                          buffer,
                                          bufferSize,
                                          &decryptedSize);
    
    NSData *result = nil;
    if (cryptStatus == kCCSuccess) {
        result = [NSData dataWithBytesNoCopy:buffer length:decryptedSize];
    } else {
        free(buffer);
    }
    
    return result;
}

@end
